﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Skugo.Shared;
using System.IO;

namespace Skugo
{
    public delegate void ChartValueFn(Object selectedValue);

    public enum ChartType
    {
        BarsVertical,
        BarsHorizontal,
        Pie,
        LineThin,
        LineArea,
    }

    public class Chart : DockablePlugin
    {
        /// <summary>
        /// The type of message we send or receieve.
        /// </summary>
        enum MessageType : byte
        {
            ClearAllValues,
            NewValue,
            SetMaxValues,
            SetChartType,
            ValueSelected,
            ValueDoubleClicked,
        }

        public event ChartValueFn OnValueSelected;
        public event ChartValueFn OnValueDoubleClicked;

        private ChartType Type = ChartType.BarsVertical;

        private UInt32 MaxValues = UInt32.MaxValue;

        private List<KeyValuePair<Object, Object>> Values = new List<KeyValuePair<Object, Object>>();

        /// <summary>
        /// The name of the remote type we wish to instantiate.
        /// </summary>
        private const String RemoteType = "Skugo.StandardPlugins.Chart";

        public Chart(Client client, String displayName) :
            base(RemoteType, client, displayName)
        {
        }

        public void SetChartType(ChartType type)
        {
            this.Type = type;

            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            // Write out a the message
            outgoing.Write((Byte)MessageType.SetChartType);
            outgoing.Write((Byte)type);

            this.Send(stream.ToArray());
        }

        public void ClearAllValues()
        {
            this.Values.Clear();

            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            // Write out a the message
            outgoing.Write((Byte)MessageType.ClearAllValues);

            this.Send(stream.ToArray());
        }

        public void SetMaxValues(UInt32 maxValues)
        {
            this.MaxValues = maxValues;

            while (this.Values.Count > this.MaxValues)
            {
                this.Values.RemoveAt(0);
            }

            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            // Write out a the message
            outgoing.Write((Byte)MessageType.SetMaxValues);
            outgoing.Write(maxValues);

            this.Send(stream.ToArray());
        }

        private void AddValueInternal(Object independent, Object dependent)
        {
            // First add the value to the list of values
            this.Values.Add(new KeyValuePair<Object, Object>(independent, dependent));

            if (this.Values.Count > this.MaxValues)
            {
                this.Values.RemoveAt(0);
            }

            this.SendAddValueMessage(independent, dependent);
        }

        private void SendAddValueMessage(Object independent, Object dependent)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            // Write out a the message
            outgoing.Write((Byte)MessageType.NewValue);
            outgoing.WritePrimitiveDynamic(independent);
            outgoing.WritePrimitiveDynamic(dependent);

            this.Send(stream.ToArray());
        }

        public void AddValue(String independent, Int32 dependent)
        {
            this.AddValueInternal(independent, dependent);
        }

        public void AddValue(String independent, Single dependent)
        {
            this.AddValueInternal(independent, dependent);
        }

        public void AddValue(Single independent, Single dependent)
        {
            this.AddValueInternal(independent, dependent);
        }

        public void AddValue(Single independent, Int32 dependent)
        {
            this.AddValueInternal(independent, dependent);
        }

        public void AddValue(Int32 independent, Single dependent)
        {
            this.AddValueInternal(independent, dependent);
        }

        public void AddValue(Int32 independent, Int32 dependent)
        {
            this.AddValueInternal(independent, dependent);
        }

        // Called when packet data is received from our external counterpart
        protected override void OnReceive(Byte[] data)
        {
            var incoming = data.ToBinaryReader();

            var messageType = (MessageType)incoming.ReadByte();

            switch (messageType)
            {
                case MessageType.ValueSelected:
                {
                    Object selectedValue = incoming.ReadPrimitiveDynamic();

                    if (this.OnValueSelected != null)
                    {
                        this.OnValueSelected(selectedValue);
                    }
                    break;
                }

                case MessageType.ValueDoubleClicked:
                {
                    Object selectedValue = incoming.ReadPrimitiveDynamic();

                    if (this.OnValueDoubleClicked != null)
                    {
                        this.OnValueDoubleClicked(selectedValue);
                    }
                    break;
                }

                default:
                    throw new Exception("Unhandled message type!");
            }
        }
    }
}
